package be.android.ocb.smartcard;

import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.Util;
import javacard.security.CryptoException;
import javacard.security.HMACKey;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
import javacard.security.Signature;
import javacardx.crypto.Cipher;
import javacardx.framework.util.ArrayLogic;

public class AesOcb extends Applet{

	private final static byte DERIVE_KEY = (byte)0x20; 
	private final static byte GET_KEY = (byte)0x21; 
	
	private final static byte ENCRYPT = (byte)0x60;
	private final static byte DECRYPT = (byte)0x61;

	short counter = 0x30;

	public static void install(byte[] aArray, short	sOffset, byte bLength){
		new AesOcb(aArray, sOffset, bLength);
	}

	private AesOcb(byte[] aArray, short sOffset, byte bLength){
		register(aArray, (short) (sOffset + 1),	aArray[sOffset]);
	}
	//implementation for select() and deselect()
	//from MultiSelectable interface
	public boolean select(boolean appInstAlreadyActive){
		return true;
	}
	public void deselect(boolean appInstStillActive){
		return;
	}

	public void process(APDU apdu) throws ISOException {
		if (selectingApplet())
			return;
		byte[] buffer = apdu.getBuffer();
		if ( ((byte)(buffer[ISO7816.OFFSET_CLA] & (byte)0xFC)) != (byte)0x90 )	{
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		}
		switch (buffer[ISO7816.OFFSET_INS]) {
		case (byte) 0x10:
			buffer[0] = (byte) 'H';
		buffer[1] = (byte) 'e';
		buffer[2] = (byte) 'l';
		buffer[3] = (byte) 'l';
		buffer[4] = (byte) 'o';
		buffer[5] = (byte) ',';
		buffer[6] = (byte) ' ';
		buffer[7] = (byte) 'S';
		buffer[8] = (byte) 'm';
		buffer[9] = (byte) 'a';
		buffer[10] = (byte) 'r';
		buffer[11] = (byte) 't';
		buffer[12] = (byte) 'c';
		buffer[13] = (byte) 'a';
		buffer[14] = (byte) 'r';
		buffer[15] = (byte) 'd';
		buffer[16] = (byte) ':';
		buffer[17] = (byte) ' ';
		// increase counter by one and return counter value
		buffer[18] = (byte) (counter++ % 10 + 0x30);
		apdu.setOutgoingAndSend((short) 0, (short) 19);
		break;
		case DERIVE_KEY:
			deriveKey(apdu);
			break;
		case GET_KEY:
			getKey(apdu);
			break;
		case ENCRYPT:
			encrypt(apdu);
			break;
		case DECRYPT:
			decrypt(apdu);
			break;
		default:
			ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
		}
	}
	
	
	
	
	
	private void deriveKey(APDU apdu) {
		byte[] outbuffer;
		short outLength;
		try{
			byte[] apduBuffer = apdu.getBuffer();
			short dataLen = apdu.setIncomingAndReceive();
			outbuffer = generatePBEKey(apduBuffer);
			outLength = (short) outbuffer.length;
		}catch(CryptoException e){
			if (e.getReason() == CryptoException.NO_SUCH_ALGORITHM){
				outbuffer = new byte[]{(byte)'N' ,(byte)'o' ,(byte)' ' ,(byte)'A' ,(byte)'l' ,(byte)'g' ,(byte)'o' ,(byte)'r', (byte)'i' } ;
				outLength = (short)outbuffer.length;
			}
			outbuffer = new byte[]{(byte)'N' ,(byte)'o' ,(byte)' ' ,(byte)'S' ,(byte)'e' ,(byte)'c' ,(byte)'r' ,(byte)'e', (byte)'t' } ;
			outLength = (short)outbuffer.length;
		}		
		apdu.setOutgoing();
		apdu.setOutgoingLength(outLength);
		apdu.sendBytesLong(outbuffer, (short) 0, outLength);
		
	}

	private void getKey(APDU apdu) {
		// TODO Auto-generated method stub
		
	}

	private void encrypt(APDU apdu) {
		// TODO Auto-generated method stub
		
	}

	private void decrypt(APDU apdu) {
		// TODO Auto-generated method stub
		
	}





	public static final int BLOCK_BITS  = 128;

	protected static final int  PRE_COMP_BLOCKS = 32;
	public static final int BLOCK_SIZE  = (BLOCK_BITS >>> 3);
	protected static final int  TAG_LENGTH = BLOCK_SIZE;
	private class L{
	private byte[]  Lblock      = new byte[16];
	public byte[] getLblock() {
		return Lblock;
	}
}

L[]  Lprecomp = new L[PRE_COMP_BLOCKS];

	//protected byte[][]  L       = new byte[PRE_COMP_BLOCKS][BLOCK_SIZE]; // Precomputed L(i) values.
	protected byte[]    L_inv   = new byte[BLOCK_SIZE]; // Precomputed L/x value
	protected byte[]    tmp     = new byte[BLOCK_SIZE];
	protected byte[]    offset  = new byte[BLOCK_SIZE]; // Offset (Z[i]) for current block
	protected byte[]    chksum  = new byte[TAG_LENGTH]; // Checksum for computing tag


	/*public static byte[] calculateRFC2104HMAC(byte[] data, byte[] key) {
		byte[] result;
		short blockSize = 64; //SHA-1 64 bytes blocksize
		MessageDigest sha_1 = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
		byte[] key2 = new byte[sha_1.getLength()];
		if(key.length>blockSize){			
			short key2Length = sha_1.doFinal(key, (short)0, (short)key.length, key2, (short) 0);
		}
		if(key.length<blockSize){
			Util.arrayCopy(key, (short)0, key2, (short)0, (short)key.length);
			Util.arrayFillNonAtomic(key2, (short)key.length, (short)(blockSize - key.length), (byte)0);
		}
		
		
		
}*/

	HMACKey hmacK;


	public byte[] generatePBEKey(byte[] password) {
		byte salt[] = new byte[16];
		//SecureRandom saltGen = SecureRandom.getInstance("SHA1PRNG");
		RandomData saltGen = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
		//saltGen.nextBytes(salt);
		saltGen.generateData(salt, (short)0, (short)salt.length);

		//DropboxUtil.uploadFile(salt, Constants.SALT_FILE);
		//Log.e(TAG, "salt "+ salt);
		//http://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption
		/**
		 *  Derive the key, given password and salt. 
		 */
		//SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
		//HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, KeyBuilder.LENGTH_HMAC_SHA_1_BLOCK_64, true);
		//key.setKey(arg0, arg1, arg2)
		hmacK = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, KeyBuilder.LENGTH_HMAC_SHA_1_BLOCK_64, true);
    	hmacK.setKey(password, (short)0, (short) password.length);
		byte[] derivedKey = PBKDF2(salt,655536,128);
		return derivedKey;
		//KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
		//SecretKey tmp = factory.generateSecret(spec);
		//Log.e(TAG,tmp.getEncoded().length + " "+ new String(tmp.getEncoded()));
		//SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
		//Log.e(TAG,secret.getEncoded().length + " "+ new String(secret.getEncoded()));
		//return secret.getEncoded();
	}


	/**
	 * Core Password Based Key Derivation Function 2.
	 * 
	 * @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2</a>
	 * @param prf
	 *            Pseudo Random Function (i.e. HmacSHA1)
	 * @param S
	 *            Salt as array of bytes. <code>null</code> means no salt.
	 * @param c
	 *            Iteration count (see RFC 2898 4.2)
	 * @param dkLen
	 *            desired length of derived key.
	 * @return internal byte array
	 */
	protected byte[] PBKDF2(byte[] S, int c, int dkLen)
	{
		if (S == null)
		{
			S = new byte[0];
		}
		int hLen = KeyBuilder.LENGTH_HMAC_SHA_1_BLOCK_64;
		int l = ceil(dkLen, hLen);
		int r = dkLen - (l - 1) * hLen;
		byte T[] = new byte[l * hLen];
		int ti_offset = 0;
		for (int i = 1; i <= l; i++)
		{
			_F(T, ti_offset, S, c, i);
			ti_offset += hLen;
		}
		if (r < hLen)
		{
			// Incomplete last block
			byte DK[] = new byte[dkLen];
			Util.arrayCopy(T, (short)0, DK, (short)0, (short)dkLen);
			return DK;
		}
		return T;
	}



	/**
	 * Integer division with ceiling function.
	 * 
	 * @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step 2.</a>
	 * @param a
	 * @param b
	 * @return ceil(a/b)
	 */
	private int ceil(int a, int b)
	{
		int m = 0;
		if (a % b > 0)
		{
			m = 1;
		}
		return a / b + m;
	}

	/**
     * Function F.
     * 
     * @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step 3.</a>
     * @param dest
     *            Destination byte buffer
     * @param offset
     *            Offset into destination byte buffer
     * @param prf
     *            Pseudo Random Function
     * @param S
     *            Salt as array of bytes
     * @param c
     *            Iteration count
     * @param blockIndex
     */
	protected void _F(byte[] dest, int offset, byte[] S, int c,
            int blockIndex)
    {
        int hLen = KeyBuilder.LENGTH_HMAC_SHA_1_BLOCK_64;
        byte U_r[] = new byte[hLen];

        // U0 = S || INT (i);
        byte U_i[] = new byte[S.length + 4];
        Util.arrayCopy(S, (short)0, U_i, (short)0, (short)S.length);
        INT(U_i, S.length, blockIndex);

        for (int i = 0; i < c; i++){
        	
            //U_i = prf.doFinal(U_i);
        	/*HMACKey hmacK = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, KeyBuilder.LENGTH_HMAC_SHA_1_BLOCK_64, true);
        	hmacK.setKey(U_i, (short)0, (short) U_i.length);
        	hmacK.getKey(U_i, (short)0);*/
        	Signature sig = Signature.getInstance(Signature.ALG_HMAC_SHA1, false);
        	sig.init(hmacK, Signature.MODE_SIGN);
        	sig.sign(U_i, (short)0, (short)U_i.length, U_i, (short)0);
        	
            xor(U_r, U_i);
        }
        Util.arrayCopy(U_r, (short)0, dest, (short)offset, (short)hLen);
    }
	
	/**
     * Block-Xor. Xor source bytes into destination byte buffer. Destination
     * buffer must be same length or less than source buffer.
     * 
     * @param dest
     * @param src
     */
    protected void xor(byte[] dest, byte[] src)
    {
        for (int i = 0; i < dest.length; i++)
        {
            dest[i] ^= src[i];
        }
    }

    /**
     * Four-octet encoding of the integer i, most significant octet first.
     * 
     * @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step 3.</a>
     * @param dest
     * @param offset
     * @param i
     */
    protected void INT(byte[] dest, int offset, int i)
    {
        dest[offset + 0] = (byte) (i / (256 * 256 * 256));
        dest[offset + 1] = (byte) (i / (256 * 256));
        dest[offset + 2] = (byte) (i / (256));
        dest[offset + 3] = (byte) (i);
    }
	
	/***
	 *  function ocb-aes-encrypt(K, M, Nonce)
    begin
        Offset = AES(K, Nonce xor L) 
        Checksum = 0
        for i = 1 to m-1 do begin
            Offset = Offset xor L(ntz(i))
            Checksum = Checksum xor M[i]
            C[i] = Offset xor AES(K, M[i] xor Offset) 
        end
        Offset = Offset xor L(ntz(m))
        Pad = AES (K, len(M[m]) xor L(-1) xor Offset)
        C[m] = M[m] xor (the first |M[m]| bits of Pad)
        Checksum = Checksum xor Pad xor C[m]0*
        FullTag = AES(K, Checksum xor Offset) 
        Tag = a prefix of FullTag (of the desired length)
        return C[1]... C[m-1] C[m] Tag 
    end
	 */

	/*public byte[] encrypt(byte[] pt, int ptLen, byte[] nonce, byte[] tag){

		int ptPos = 0;

		// Create ciphertext
		byte[] ct = new byte[ptLen];
		int ctPos = 0;

		//Arrays.fill(chksum, (byte)0);                       // Zero the checksum
		chksum[0] = (byte)0;
		ArrayLogic.arrayFillGenericNonAtomic(chksum, (short)0, (short)chksum.length, chksum, (short)0);

		xorBlock(offset, L[0], nonce);          			// Calculate R, aka Z[0]
		offset = encryptAes(offset);


		// Process blocks 1 .. m-1
		int i;
		for (i = 1; ptLen > BLOCK_SIZE; i++) {
			xorBlock(offset, offset, L[ntz(i)]);			// Update the offset (Z[i] from Z[i-1])
			xorBlock(chksum, chksum, pt, ptPos);			// Update the checksum
			xorBlock(tmp, offset, pt, ptPos);				// xor the plaintext block with Z[i]
			tmp = encryptAes(tmp);							// Encipher the block
			xorBlock(ct, ctPos, offset, tmp);				// xor Z[i] again, writing result to ciphertext pointer
			ptLen -= BLOCK_SIZE;
			ptPos += BLOCK_SIZE;
			ctPos += BLOCK_SIZE;
		}

		// Process block m
		xorBlock(offset, offset, L[ntz(i)]);                // Update the offset (Z[m] from Z[m-1])
		xorBlock(tmp, offset, L_inv);                       // xor L . x^{-1} and Z[m]
		tmp[BLOCK_SIZE - 1] ^= (byte)(ptLen << 3);          // Add in final block bit-length
		tmp = encryptAes(tmp);

		for (int t = 0; t < ptLen; t++) {
			ct[ctPos + t] = (byte)(pt[ptPos + t] ^ tmp[t]); // xor pt with block-cipher output to get ct
			tmp[t] = pt[ptPos + t];                         // Add to checksum the ptLen bytes of plaintext...
		}
		xorBlock(chksum, chksum, tmp);                      // ... followed by the last (16 - ptLen) bytes of block-cipher output

		// Calculate tag
		xorBlock(chksum, chksum, offset);
		tmp = encryptAes(chksum);
		Util.arrayCopy(tmp, (short)0, tag, (short)0, (short)TAG_LENGTH);

		return ct;
	}

	/**
	 * Given a nonce starting at offset noncePos of the nonce byte array,
	 * decrypt ctLen elements of byte array ct starting at offset ctPos, and
	 * verifying a tag starting at offset tagPos of the tag byte array.
	 *
	 * @param       ct          ciphertext buffer.
	 * @param       ctPos       start index of ciphertext on ct.
	 * @param       ctLen       byte length of ciphertext.
	 * @param       nonce       nonce array (BLOCK_SIZE bytes).
	 * @param       noncePos    start index of nonce.
	 * @param       tag         input tag buffer.
	 * @param       tagPos      start index of tag.
	 *
	 * @return      the resulting plaintext, a byte array of same length as ct
	 *              if decryption is successfull, or else null if the tag does
	 *              not correctly verify.
	 * 
	 * @warning     ct, nonce, and tag must not overlap.
	 */
//	public final byte[] decrypt(byte[] ct, int ctLen, byte[] nonce,  byte[] tag)
	/*throws IllegalArgumentException {

	/*	int ctPos = 0;
		// Create plaintext
		byte[] pt = new byte[ctLen];
		int ptPos = 0;

		//Arrays.fill(chksum, (byte)0);               // Zero checksum
		chksum[0] = (byte)0;
		ArrayLogic.arrayFillGenericNonAtomic(chksum, (short)0, (short)chksum.length, chksum, (short)0);

		xorBlock(offset, L[0], nonce);    			// Calculate R, aka Z[0]
		offset = encryptAes(offset);

		// Process blocks 1 .. m-1
		int i;
		for (i = 1; ctLen > BLOCK_SIZE; i++) {
			xorBlock(offset, offset, L[ntz(i)]);            // Update the offset (Z[i] from Z[i-1])
			xorBlock(tmp, offset, ct, ctPos);               // xor ciphertext block with Z[i]
			tmp = decryptAes(tmp);                          // Decipher the next block-cipher block

			xorBlock(pt, ptPos, offset, tmp);               // xor Z[i] again, writing result to plaintext pointer
			xorBlock(chksum, chksum, pt, ptPos);            // Update the checksum
			ctLen -= BLOCK_SIZE;
			ctPos += BLOCK_SIZE;
			ptPos += BLOCK_SIZE;
		}

		// Process block m
		xorBlock(offset, offset, L[ntz(i)]);                // Update the offset (Z[m] from Z[m-1])
		xorBlock(tmp, offset, L_inv);                       // xor L . x^{-1} and Z[m]
		tmp[BLOCK_SIZE - 1] ^= (byte)(ctLen << 3);          // Add in final block bit-length
		tmp = encryptAes(tmp);

		for (int t = 0; t < ctLen; t++) {
			pt[ptPos + t] = (byte)(ct[ctPos + t] ^ tmp[t]); // xor ct with block-cipher output to get pt
			tmp[t] = pt[ptPos + t];                         // Add to checksum the ctLen bytes of plaintext...
		}
		xorBlock(chksum, chksum, tmp);                      // ... followed by the last (16 - ptLen) bytes of block-cipher output

		// Calculate and verify tag
		xorBlock(chksum, chksum, offset);
		tmp = encryptAes(chksum);
		for (int t = 0; t < TAG_LENGTH; t++) {
			if (tmp[t] != tag[0 + t]) {
				//Arrays.fill(pt, (byte)0);
				pt[0] = (byte)0;
				ArrayLogic.arrayFillGenericNonAtomic(pt, (short)0, (short)pt.length, pt, (short)0);

				pt = null;
				break;
			}
		}

		return pt;
	}

	//SecretKey skey;
	//SecretKeySpec skeySpec;
	byte[] aesOcbKey = new byte[16];

	/**
	 * Initialise the underlying AES cipher with a symmetric key.
	 *
	 * @param   cipherKey   the symmetric AES key.
	 * @param   keyBits     the key size in bits (128, 192, or 256).
	 *
	 * @throws  IllegalArgumentException    if the key is null or
	 *                      its length is not 128, 192, or 256.
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeySpecException 
	 */
	/*public final void init(byte[] k, int keyBits){
	///*throws IllegalArgumentException, NoSuchAlgorithmException, InvalidKeySpecException*/
	/*	if (k == null) {
			//TODO
			//throw new IllegalArgumentException("Null key");
		}
		//if (keyBits != 128 && keyBits != 192 && keyBits != 256) {
		//throw new IllegalArgumentException("Invalid AES key size: " + keyBits + " bits.");
		//}
		// Initialize AES keys
		aesOcbKey = k;

		// Precompute L[i]-values (L[0] is synonym of L)
		byte[] L0 = L[0];
		//Arrays.fill(L0, (byte)0);
		L0[0] = (byte)0;
		ArrayLogic.arrayFillGenericNonAtomic(L0, (short)0, (short)L0.length, L0, (short)0);


		//aes.encrypt(L0, L0);
		L0 = encryptAes(L0);

		for (int i = 1; i < PRE_COMP_BLOCKS; i++) {
			// L[i] = L[i - 1] * x
			byte[] L_cur = L[i], L_pre = L[i - 1];
			for (int t = 0; t < BLOCK_SIZE - 1; t++) {
				L_cur[t] = (byte)((L_pre[t] << 1) | ((L_pre[t + 1] >>> 7) & 1));
			}
			L_cur[BLOCK_SIZE - 1] = (byte)((L_pre[BLOCK_SIZE - 1] << 1) ^ ((L_pre[0] & 0x80) != 0 ? 0x87 : 0));
		}

		// Precompute L_inv = L / x = L * x^{-1}
		for (int i = BLOCK_SIZE - 1; i > 0; i--) {
			L_inv[i] = (byte)(((L0[i] & 0xff) >>> 1) | ((L0[i - 1] & 1) << 7));
		}
		L_inv[0] = (byte)((L0[0] & 0xff) >>> 1);
		if ((L0[BLOCK_SIZE - 1] & 1) != 0) {
			L_inv[0] ^= 0x80;
			L_inv[BLOCK_SIZE - 1] ^= 0x43;
		}
	}*/

	/*protected static final void xorBlock(byte[] dst, int pos,
			byte[] src1, byte[] src2) {
		for (int i = 0; i < BLOCK_SIZE; i++) {
			dst[(short)pos + i] = (byte)(src1[i] ^ src2[i]);
		}
	}

	protected static final void xorBlock(byte[] dst, byte[] src1,
			byte[] src2, int pos) {
		for (int i = 0; i < BLOCK_SIZE; i++) {
			dst[i] = (byte)(src1[i] ^ src2[pos + i]);
		}
	}

	protected static final void xorBlock(byte[] dst, byte[] src1, byte[] src2) {
		for (int i = 0; i < BLOCK_SIZE; i++) {
			dst[i] = (byte)(src1[i] ^ src2[i]);
		}
	}*/

	/**
	 * Count the number of trailing zeroes in integer i.
	 *
	 * @return  0 <= ntz <= 32.
	 */
	protected static final int ntz(int i) {
		/*
		 * N.B. this function is not as bad as it might seem:
		 * assuming the argument i is uniformly distributed,
		 * the probability that ntz(i) = k is 2^(-k-1),
		 * therefore the expected value of ntz(i) is < 1.
		 */
		int result = 0;
		while ((i & 1) == 0) {
			i >>>= 1;
			result++;
		}
		return result;
	}

	private final byte AESOCB_ALG = Cipher.ALG_AES_BLOCK_128_ECB_NOPAD;

	private byte[] encryptAes(byte[] src){
		byte[] result = null;

		Cipher encryptCipher = Cipher.getInstance(AESOCB_ALG, false);
		//encryptCipher.init(aesOcbKey, Cipher.MODE_ENCRYPT);
		encryptCipher.doFinal(src, (short) 0, (short) src.length, result, (short) 0);

		/*try {
			SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
			result = cipher.doFinal(src);
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		return result;
	}

	private byte[] decryptAes(byte[] src){
		byte[] result = null;
		/*try {
			//KeyGenerator kgen = KeyGenerator.getInstance("AES");
			//kgen.init(128); // 192 and 256 bits may not be available
			// Generate the secret key specs.
			//skey = kgen.generateKey();
			//byte[] raw = skey.getEncoded();
			SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
			cipher.init(Cipher.DECRYPT_MODE, skeySpec);
			result = cipher.doFinal(src);
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		return result;
	}


}
